﻿using System;
using System.Reactive.Concurrency;
using StreamBase.SB.Client;

namespace Sbcx {

    public sealed class ObservableOptions {

        static readonly TimeSpan __defaultInterval = TimeSpan.FromMilliseconds (
            StreamBaseClient.DEFAULT_FLUSH_INTERVAL * 4);
        static readonly ObservableOptions __lowLatency = new ObservableOptions (
            Scheduler.Immediate, Scheduler.NewThread, null, __defaultInterval);
        static readonly ObservableOptions __default = new ObservableOptions (
            Scheduler.CurrentThread, Scheduler.ThreadPool, __defaultInterval, __defaultInterval);

        public ObservableOptions (IScheduler subjectScheduler, IScheduler dueTimeScheduler, TimeSpan? pollInterval, TimeSpan timeout) {
            if (timeout.TotalMilliseconds > int.MaxValue || timeout <= TimeSpan.Zero)
                throw new ArgumentOutOfRangeException ("timeout");
            if (pollInterval.HasValue && pollInterval.Value <= TimeSpan.Zero)
                throw new ArgumentOutOfRangeException ("pollInterval");
            if (null == subjectScheduler)
                throw new ArgumentNullException ("subjectScheduler");
            if (null == dueTimeScheduler)
                throw new ArgumentNullException ("dueTimeScheduler");

            SubjectScheduler = subjectScheduler;
            DueTimeScheduler = dueTimeScheduler;
            PollInterval = pollInterval;
            Timeout = timeout;
        }

        public ObservableOptions (IScheduler subjectScheduler, IScheduler dueTimeScheduler, TimeSpan? pollInterval)
            : this (subjectScheduler, dueTimeScheduler, pollInterval, __defaultInterval) {
        }

        /// <summary>
        /// Scheduler used internally when publishing new values
        /// </summary>
        public IScheduler SubjectScheduler { get; private set; }
        /// <summary>
        /// Scheduler to use when DueTime is specified to schedule Dequeue calls
        /// </summary>
        public IScheduler DueTimeScheduler { get; private set; }
        /// <summary>
        /// Optionally specifies to poll at this interval
        /// </summary>
        public TimeSpan? PollInterval { get; private set; }
        /// <summary>
        /// Time to wait during the call to <see cref="StreamBaseClient.Dequeue"/>
        /// </summary>
        public TimeSpan Timeout { get; private set; }

        /// <summary>
        /// Preconfigured options to spin up a new thread for dequeueing from a stream
        /// and using <see cref="Scheduler.Immediate"/> for publishing values
        /// </summary>
        /// <remarks>
        /// A dequeue timeout of 10 seconds decreases latency and takes longer to abort
        /// </remarks>
        public static ObservableOptions LowLatency {
            get {
                return __lowLatency;
            }
        }

        /// <summary>
        /// Preconfigured options to use the .NET thread pool for dequeueing from a stream
        /// and using the <see cref="Scheduler.CurrentThread"/> for publishing values
        /// </summary>
        public static ObservableOptions Default {
            get {
                return __default;
            }
        }

        internal int TimeoutMS {
            get {
                return (int)Timeout.TotalMilliseconds;
            }
        }
    }
}